use alloc::collections::LinkedList;

use rtt_kernel_mutex::KMutex;

use crate::alloc::boxed::Box;
use crate::global::{CURRENT_THREAD, MAX_PRIORITY};
use crate::scheduler::rt_schedule;
use crate::thread::{rt_thread_weak, ObjThread};

struct RawSem {
    flag: usize,
    threads: [LinkedList<Box<ObjThread>>; MAX_PRIORITY],
}

unsafe impl Send for Sem {}
unsafe impl Sync for Sem {}

pub struct Sem {
    raw: KMutex<RawSem>,
}

impl Sem {
    pub const fn new() -> Sem {
        const VOID_LIST: LinkedList<Box<ObjThread>> = LinkedList::new();
        Sem {
            raw: KMutex::new(RawSem {
                flag: 0,
                threads: [VOID_LIST; MAX_PRIORITY],
            }),
        }
    }

    fn not_empty_list(&self) -> Option<usize> {
        let list = self.raw.lock();
        for i in 0..MAX_PRIORITY {
            if !list.threads[i].is_empty() {
                return Some(i);
            }
        }
        None
    }

    pub fn release(&mut self) {
        let mut raw = self.raw.lock();
        if raw.flag == 0 {
            match self.not_empty_list() {
                None => raw.flag += 1,
                Some(i) => {
                    let th = raw.threads[i].pop_front().unwrap();
                    rt_thread_weak(th);
                }
            }
        } else {
            raw.flag += 1;
        }
    }

    pub fn take(&mut self) {
        loop {
            let mut raw = self.raw.lock();
            if raw.flag > 0 {
                raw.flag -= 1;
                return;
            } else {
                let mut th = CURRENT_THREAD.lock();
                let cur_th = th.get_thread_set_sp().unwrap();
                let cur_th_pri = cur_th.get_priority();
                raw.threads[cur_th_pri].push_back(cur_th);
                rt_schedule();
            }
        }
    }
}

pub type MLibSem = Sem;
